/*
 * Copyright (C) 2007-2012 Geometer Plus <contact@geometerplus.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

package org.geometerplus.zlibrary.text.view;

import android.util.Log;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.core.view.SelectionCursor;
import org.geometerplus.zlibrary.text.view.ZLTextRegion.Soul;

public class ZLTextSelection extends ZLTextHighlighting {
    public static class Point {
        public int X;
        public int Y;

        Point(int x, int y) {
            X = x;
            Y = y;
        }
    }

    private final ZLTextView myView;

    private ZLTextRegion.Soul myLeftMostRegionSoul;
    private ZLTextRegion.Soul myRightMostRegionSoul;

    private SelectionCursor.Which myCursorInMovement = null;
    private final Point myCursorInMovementPoint = new Point(-1, -1);

    private Scroller myScroller;

    private ZLColor mStyleColor = new ZLColor(0xfc5d4f);
    public String id = "l" + System.currentTimeMillis();
    protected boolean isUnderLine = false;

    public boolean isUnderLine() {
        return isUnderLine;
    }

    public void setUnderLine(boolean isUnderLine) {
        this.isUnderLine = isUnderLine;
    }

    public ZLTextSelection(ZLTextView view) {
        myView = view;
        isUnderLine = false;
        mStyleColor = SystemSettings.getInstance().getLastSelectPopUpColor();
        if (mStyleColor == null) {
            mStyleColor = new ZLColor(0xfc5d4f);
        }
    }


    public ZLTextSelection(ZLTextView view, LocalDumpNote note) {
        myView = view;
        id = note.note_Id;
        myLeftMostRegionSoul = new Soul(note.mostLeftPositionParaghIdx, note.mostLeftPositionStartElementIdx, note.mostLeftPositionEndElementIdx);
        myRightMostRegionSoul = new Soul(note.mostRightPositionParaghIdx, note.mostRightPositionStartElementIdx, note.mostRightPositionEndElementIdx);
        mStyleColor = new ZLColor((note.mStyleColor >> 16) & 0xFF, (note.mStyleColor >> 8) & 0xFF, (note.mStyleColor) & 0xFF, (note.mStyleColor >> 24) & 0xFF);
        isUnderLine = true;
        mNote = note;
    }

    public ZLTextSelection(ZLTextView view, BookReadNote note) {
        myView = view;
        this.mNote = note;
        id = note.note_Id;
        myLeftMostRegionSoul = new Soul(note.mostLeftPositionParaghIdx, note.mostLeftPositionStartElementIdx, note.mostLeftPositionEndElementIdx);
        myRightMostRegionSoul = new Soul(note.mostRightPositionParaghIdx, note.mostRightPositionStartElementIdx, note.mostRightPositionEndElementIdx);
        mStyleColor = new ZLColor((note.mStyleColor >> 16) & 0xFF, (note.mStyleColor >> 8) & 0xFF, (note.mStyleColor) & 0xFF, (note.mStyleColor >> 24) & 0xFF);
        isUnderLine = true;
    }

    public ZLTextSelection(ZLTextView view, ZLTextFixedPosition startPos, ZLTextFixedPosition endPos)
    {
        myLeftMostRegionSoul = new Soul(startPos.ParagraphIndex, startPos.ElementIndex, startPos.ElementIndex);
        myRightMostRegionSoul = new Soul(endPos.ParagraphIndex, endPos.ElementIndex, endPos.ElementIndex);
        mStyleColor = new ZLColor(0xfc5d4f);
        myView = view;

    }
    public void setPosition(ZLTextFixedPosition startPos, ZLTextFixedPosition endPos)
    {
        if(myLeftMostRegionSoul == null)
        {
            myLeftMostRegionSoul = new Soul(startPos.ParagraphIndex, startPos.ElementIndex, startPos.ElementIndex);
        }
        else
        {
            myLeftMostRegionSoul.ParagraphIndex = startPos.ParagraphIndex;
            myLeftMostRegionSoul.StartElementIndex = startPos.ElementIndex;
            myLeftMostRegionSoul.EndElementIndex = startPos.ElementIndex;
        }

        if(myRightMostRegionSoul == null)
        {
            myRightMostRegionSoul = new Soul(endPos.ParagraphIndex, endPos.ElementIndex, endPos.ElementIndex);
        }
        else
        {

            myRightMostRegionSoul.ParagraphIndex = endPos.ParagraphIndex;
            myRightMostRegionSoul.StartElementIndex = endPos.ElementIndex;
            myRightMostRegionSoul.EndElementIndex = endPos.ElementIndex;
        }

        mStyleColor = new ZLColor(0xfc5d4f);

    }

    @Override
    public boolean isEmpty() {
        return myLeftMostRegionSoul == null || myRightMostRegionSoul == null;
    }

    public boolean clear() {
        if (isEmpty()) {
            return false;
        }

        stop();
        isUnderLine = false;
        myLeftMostRegionSoul = null;
        myRightMostRegionSoul = null;
        myCursorInMovement = null;
        return true;
    }

    void setCursorInMovement(SelectionCursor.Which which, int x, int y) {
        myCursorInMovement = which;
        myCursorInMovementPoint.X = x;
        myCursorInMovementPoint.Y = y;
    }

    SelectionCursor.Which getCursorInMovement() {
        return myCursorInMovement;
    }

    public Point getCursorInMovementPoint() {
        return myCursorInMovementPoint;
    }

    public boolean start(int x, int y) {
        clear();

        final ZLTextRegion region = myView.findRegion(
                x, y, myView.maxSelectionDistance(), ZLTextRegion.AnyRegionFilter
        );
        if (region == null) {
            return false;
        }

        myRightMostRegionSoul = myLeftMostRegionSoul = region.getSoul();
        return true;
    }

    public boolean isStoped()
    {
        return myCursorInMovement == null;
    }
    public void stop() {
        myCursorInMovement = null;
        if (myScroller != null) {
            myScroller.stop();
            myScroller = null;
        }
    }

    void expandTo(ZLTextPage page, int x, int y) {
        if (isEmpty()) {
            return;
        }

        final ZLTextElementAreaVector vector = page.TextElementMap;
        final ZLTextElementArea firstArea = vector.getFirstArea();
        final ZLTextElementArea lastArea = vector.getLastArea();
        if (firstArea != null && y < firstArea.YStart) {
            if (myScroller != null && myScroller.scrollsForward()) {
                myScroller.stop();
                myScroller = null;
            }
            if (myScroller == null) {
                myScroller = new Scroller(page, false, x, y);
                return;
            }
            //} else if (lastArea != null && y + ZLTextSelectionCursor.getHeight() / 2 + ZLTextSelectionCursor.getAccent() / 2 > lastArea.YEnd) {
        } else if (lastArea != null && y > lastArea.YEnd) {
            if (myScroller != null && !myScroller.scrollsForward()) {
                myScroller.stop();
                myScroller = null;
            }
            if (myScroller == null) {
                myScroller = new Scroller(page, true, x, y);
                return;
            }
        } else {
            if (myScroller != null) {
                myScroller.stop();
                myScroller = null;
            }
        }

        if (myScroller != null) {
            myScroller.setXY(x, y);
        }

        ZLTextRegion region = myView.findRegion(x, y, myView.maxSelectionDistance(), ZLTextRegion.AnyRegionFilter);
        if (region == null) {
            final ZLTextElementAreaVector.RegionPair pair =
                    myView.findRegionsPair(x, y, ZLTextRegion.AnyRegionFilter);
            if (pair.Before != null || pair.After != null) {
                final ZLTextRegion.Soul base =
                        myCursorInMovement == SelectionCursor.Which.Right
                                ? myLeftMostRegionSoul : myRightMostRegionSoul;
                if (pair.Before != null) {
                    if (base.compareTo(pair.Before.getSoul()) <= 0) {
                        region = pair.Before;
                    } else {
                        region = pair.After;
                    }
                } else {
                    if (base.compareTo(pair.After.getSoul()) >= 0) {
                        region = pair.After;
                    } else {
                        region = pair.Before;
                    }
                }
            }
        }
        if (region == null) {
            return;
        }

        final ZLTextRegion.Soul soul = region.getSoul();
        if (myCursorInMovement == SelectionCursor.Which.Right) {
            if (myLeftMostRegionSoul.compareTo(soul) <= 0) {
                myRightMostRegionSoul = soul;
            } else {
                myRightMostRegionSoul = myLeftMostRegionSoul;
                myLeftMostRegionSoul = soul;
                myCursorInMovement = SelectionCursor.Which.Left;
            }
        } else {
            if (myRightMostRegionSoul.compareTo(soul) >= 0) {
                myLeftMostRegionSoul = soul;
            } else {
                myLeftMostRegionSoul = myRightMostRegionSoul;
                myRightMostRegionSoul = soul;
                myCursorInMovement = SelectionCursor.Which.Right;
            }
        }

        if (myCursorInMovement == SelectionCursor.Which.Right) {
            if (hasPartAfterPage(page)) {
                myView.turnPage(true, ZLTextView.ScrollingMode.SCROLL_LINES, 1);
                myView.Application.getViewWidget().reset();
                myView.preparePaintInfo();
            }
        } else {
            if (hasPartBeforePage(page)) {
                myView.turnPage(false, ZLTextView.ScrollingMode.SCROLL_LINES, 1);
                myView.Application.getViewWidget().reset();
                myView.preparePaintInfo();
            }
        }
    }

    @Override
    public ZLTextPosition getStartPosition() {
        if (isEmpty()) {
            return null;
        }
        return new ZLTextFixedPosition(
                myLeftMostRegionSoul.ParagraphIndex,
                myLeftMostRegionSoul.StartElementIndex,
                0
        );
    }

    @Override
    public ZLTextPosition getEndPosition() {
        if (isEmpty()) {
            return null;
        }
        final ZLTextParagraphCursor cursor = myView.cursor(myRightMostRegionSoul.ParagraphIndex);
        if (cursor == null) {
            return null;
        }
        final ZLTextElement element = cursor.getElement(myRightMostRegionSoul.EndElementIndex);
        return new ZLTextFixedPosition(
                myRightMostRegionSoul.ParagraphIndex,
                myRightMostRegionSoul.EndElementIndex,
                element instanceof ZLTextWord ? ((ZLTextWord) element).Length : 0
        );
    }

    @Override
    public ZLTextElementArea getStartArea(ZLTextPage page) {
        if (isEmpty()) {
            return null;
        }
        final ZLTextElementAreaVector vector = page.TextElementMap;
        final ZLTextRegion region = vector.getRegion(myLeftMostRegionSoul);
        if (region != null) {
            return region.getFirstArea();
        }
        final ZLTextElementArea firstArea = vector.getFirstArea();
        if (firstArea != null && myLeftMostRegionSoul != null && myLeftMostRegionSoul.compareTo(firstArea) <= 0) {
            return firstArea;
        }
        return null;
    }

    @Override
    public ZLTextElementArea getEndArea(ZLTextPage page) {
        if (isEmpty()) {
            return null;
        }
        final ZLTextElementAreaVector vector = page.TextElementMap;
        final ZLTextRegion region = vector.getRegion(myRightMostRegionSoul);
        if (region != null) {
            return region.getLastArea();
        }
        final ZLTextElementArea lastArea = vector.getLastArea();
        if (lastArea != null && myRightMostRegionSoul != null && myRightMostRegionSoul.compareTo(lastArea) >= 0) {
            return lastArea;
        }
        return null;
    }

    public ZLTextElementArea getNoteEndArea(ZLTextPage page){
        if (isEmpty()) {
            return null;
        }
        final ZLTextElementAreaVector vector = page.TextElementMap;
        final ZLTextRegion region = vector.getRegion(myRightMostRegionSoul);
        if (region != null) {
            return region.getLastArea();
        }
        return null;
    }


    @Override
    public ZLColor getForegroundColor() {
        return null;
    }

    @Override
    public ZLColor getBackgroundColor() {
        return myView.getSelectionBackgroundColor();
    }

    @Override
    public ZLColor getOutlineColor() {
        return null;
    }

    public boolean hasPartBeforePage(ZLTextPage page) {
        if (isEmpty()) {
            return false;
        }
        final ZLTextElementArea firstPageArea = page.TextElementMap.getFirstArea();
        if (firstPageArea == null) {
            return false;
        }
        final int cmp = myLeftMostRegionSoul.compareTo(firstPageArea);
        return cmp < 0 || (cmp == 0 && !firstPageArea.isFirstInElement());
    }

    public boolean hasPartAfterPage(ZLTextPage page) {
        if (isEmpty()) {
            return false;
        }
        final ZLTextElementArea lastPageArea = page.TextElementMap.getLastArea();
        if (lastPageArea == null) {
            return false;
        }
        final int cmp = myRightMostRegionSoul.compareTo(lastPageArea);
        return cmp > 0 || (cmp == 0 && !lastPageArea.isLastInElement());
    }


    public ZLColor getStyleColor() {
        return mStyleColor;
    }

    public void setStyleColor(ZLColor styleColor) {
        mStyleColor = styleColor;
    }

    private class Scroller implements Runnable {
        private final ZLTextPage myPage;
        private final boolean myScrollForward;
        private int myX, myY;

        Scroller(ZLTextPage page, boolean forward, int x, int y) {
            myPage = page;
            myScrollForward = forward;
            setXY(x, y);
            myView.Application.addTimerTask(this, 400);
        }

        boolean scrollsForward() {
            return myScrollForward;
        }

        void setXY(int x, int y) {
            myX = x;
            myY = y;
        }


        public void run() {
            myView.turnPage(myScrollForward, ZLTextView.ScrollingMode.SCROLL_LINES, 1);
            myView.preparePaintInfo();
            expandTo(myPage, myX, myY);
            myView.Application.getViewWidget().repaintForcely();
        }

        private void stop() {
            myView.Application.removeTimerTask(this);
        }
    }

    private Point mPt;

    public void setNotePosition(Point pt) {
        mPt = pt;
    }

    public Point getNotePosition() {
        return mPt;
    }

    private INoteBean mNote;

    public INoteBean getNoteData() {
        return mNote;
    }

    public void setBookReadNote(INoteBean note) {
        mNote = note;
    }

    @Override
    public boolean equals(Object o) {
        if(null != o){
            return  false;
        }
        if (this == o) {
            return true;
        }

        if (mNote != null) {
            ZLTextSelection r = (ZLTextSelection) o;
            if (r.getNoteData() != null && r.getNoteData().equals(mNote)) {
                return true;
            }
        } else {
            return id == ((ZLTextSelection) o).id;
        }
        return false;
    }

    public boolean containsFully(ZLTextSelection node) {
        if (node == null) {
            return false;
        }
        if (isEmpty()) {
            return node.isEmpty();
        }
        if (node.isEmpty()) {
            return false;
        }
        ZLTextPosition startPoint = getStartPosition();
        ZLTextPosition endPoint = getEndPosition();

        Log.d("Note", "(" + startPoint.getParagraphIndex() + "");

        return node.getEndPosition().compareTo(endPoint) <= 0 && node.getStartPosition().compareTo(startPoint) >= 0;
    }


    public ZLTextRegion.Soul getMostLeftSoul() {
        return myLeftMostRegionSoul;
    }

    public ZLTextRegion.Soul getMostRightSoul() {
        return myRightMostRegionSoul;
    }

    public boolean start(ZLTextRegion startRegion, ZLTextRegion endRegion)
    {
        clear();
        if (startRegion == null || endRegion == null) {
            return false;
        }
        myLeftMostRegionSoul = startRegion.getSoul();
        myRightMostRegionSoul = endRegion.getSoul();
        return true;
    }


}
